package com.leadiv.openrule;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

/**
 * Holds a collection of pixels per tick for each "level". A level is a layer of
 * ticks that render at a certain size length on the TickBar. PPT stands for
 * Pixels Per Tick. Each level has a PPT associated with it.
 * @author leadiv
 */
public class TickPolicy {
    private ArrayList<Float> levels;
    private float resolution;   // Pixels Per Unit
    private String unitLabel;

    public TickPolicy() {
        this.levels = new ArrayList<Float>();
        this.resolution = 1;
        this.unitLabel = "px";
    }

    public TickPolicy(String name, float resolution, String levels) {
        this.levels = new ArrayList<Float>();
        this.resolution = resolution;
        this.unitLabel = name;
        this.parseLevels(levels);
    }

    public String getLoadString() {
        String output = "";
        Iterator<Float> itr = this.levels.iterator();

        while(itr.hasNext()) {
            output += itr.next().toString() + ":";
        }
        
        return output;
    }

    /**
     * Alias of parseLevels
     * @param l see parseLevels
     */
    public void loadLevels( String l ) {
        parseLevels( l );
    }

    /**
     * Parses a level string of pixels per tick from a string having the format of:
     * [type|]level:[type|]level:...:[type|]level
     * @param levels a string representing the pixels per tick
     */
    private void parseLevels(String levels) {
        String[] l = levels.split(":");
        String[] info;

        if(levels.equals("")) { return; }

        int i = 0;
        int len = l.length;
        String type = "";
        String val = "";
        for(;i<len;i++) {
            info = l[i].split("\\|");

            if(info.length > 1) {
                val = info[1];
                type = info[0];
            } else {
                this.addLevel(Float.valueOf(info[0]).floatValue());
                continue;
            }

            this.addLevel(Float.valueOf(val).floatValue(), type);
        }
    }

    /**
     * Adds a level of Pixels Per Tick.
     * @param ppt
     */
    public void addLevel(float ppt) {
        int index = Collections.binarySearch(this.levels, ppt);
        if(index >= 0) return;

        // this is a new level
        this.levels.add((-index) - 1, ppt);
    }

    public void addLevel(float convertee, String type) {
        float ppt = convertee;

        if(type.equals("float/tpu")) {
            ppt = this.resolution / convertee;
        }

        this.addLevel(ppt);
    }

    /**
     * Removes a level of Pixels Per Tick from Policy.
     * @param l The level to remove.
     * @return The removed level as an int representing the PPT.
     */
    public int removeLevel(int l) {
        return this.levels.remove(l - 1).intValue();
    }

    /**
     * The number of levels associated with this Tick Policy.
     * @return int representing the total number of levels.
     */
    public int getTotalLevels() {
        return this.levels.size();
    }

    /**
     * Returns the Pixels Per Tick associated with this level.
     * @param l The level to query.
     * @return An int representing the PPT of level l
     */
    public float getLevel(int l) {
        return this.levels.get(l - 1).floatValue();
    }

    public float getLevel(int l, String type) {
        if(type.equals("float/tpu")) {
            float ppt = this.levels.get(l - 1).floatValue();
            return this.resolution / ppt;
        }

        return this.levels.get(l - 1).floatValue();
    }

    /**
     * Returns the level that the given pixel lives in.
     * @param px The pixel in question
     * @return An integer representing the level.
     */
    public int getPxLevel(int px) {
        int level = this.getTotalLevels();

        if(px == 0) return level; // special case

        float ppt = this.getLevel(level);

        int modLevel = (int)(px / ppt) - (int)((px - 1) / ppt);

        while(modLevel == 0) {
            level--;
            if(level < 1) {
                break;
            }

            ppt = this.getLevel(level);
            modLevel = (int)(px / ppt) - (int)((px - 1) / ppt);
        }

        return level;
    }

    /**
     * Returns the unit resolution of this policy. The Unit Resolution is the
     * number of pixels per unit.
     * @return An int representing the unit resolution of this policy.
     */
    public float getUnitResolution() {
        return resolution;
    }

    /**
     * Sets the unit resolution of this policy. The Unit Resolution is the
     * number of pixels per unit.
     * @param ppu The new unit resolution to set
     */
    public void setUnitResolution(float ppu) {
        this.resolution = ppu;
    }

    public String getUnitLabel() {
        return unitLabel;
    }

    public void setUnitLabel(String unitLabel) {
        this.unitLabel = unitLabel;
    }

    protected ArrayList<Float> getLevels() {
        return levels;
    }

    protected void setLevels(ArrayList<Float> levels) {
        this.levels = levels;
    }
}
